#pragma once

#include "tinyxml2.h"
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <list>
#include <set>
#include <utility>
#include "Serializable_xml.h"

using namespace tinyxml2;

// xml serialization
namespace Serialize_xml
{

    // 以下为 class XMLSerialize, serialize和deserialize函数本体在文件最后
    class XMLSerialize
    {
    public:
        XMLDocument doc;
        XMLElement *pos;
        const char *xmlPath;

    public:
        XMLSerialize() {}
        ~XMLSerialize() {}

        void cover(const char *name, const char *xmlPath);
        void save();
        bool load(const char *name, const char *xmlPath);

        void write(bool val, const char *name);
        void write(char val, const char *name);
        void write(int val, const char *name);
        void write(float val, const char *name);
        void write(double val, const char *name);
        void write(const char *val, const char *name);
        void write(std::string val, const char *name);
        void write(UserTypeXml &obj, const char *name);

        template <typename T>
        void write(std::vector<T> &val, const char *name);
        template <typename T>
        void write(std::list<T> &val, const char *name);
        template <typename T>
        void write(std::set<T> &val, const char *name);
        template <typename K, typename V>
        void write(std::map<K, V> &val, const char *name);
        template <typename F, typename S>
        void write(std::pair<F, S> &val, const char *name);
        template <typename T, typename... Args>
        void write_args(const T &value, const Args &...args);
        void write_args();

        bool read(bool &val);
        bool read(char &val);
        bool read(int &val);
        bool read(float &val);
        bool read(double &val);
        bool read(std::string &val);
        bool read(UserTypeXml &obj);

        template <typename T>
        bool read(std::vector<T> &val);
        template <typename T>
        bool read(std::list<T> &val);
        template <typename T>
        bool read(std::set<T> &val);
        template <typename K, typename V>
        bool read(std::map<K, V> &val);
        template <typename F, typename S>
        bool read(std::pair<F, S> &val);
        template <typename T, typename... Args>
        bool read_args(T &value, Args &...args);
        bool read_args();
    };

    void XMLSerialize::cover(const char *name, const char *xmlPath)
    {
        XMLElement *root;
        if (doc.LoadFile(xmlPath) != 0)
        {
            // cout << "create" << endl;
            root = doc.NewElement("serialization");
            doc.InsertEndChild(root);
        }
        else
        {
            // cout << "追加"  << endl;
            root = doc.RootElement();
            XMLElement *userNode = root->FirstChildElement(name);
            if (userNode != NULL) // 已有则覆盖
            {
                doc.DeleteNode(userNode);
                // cout << "delete" << name << endl;
            }
        }
        pos = root;
        this->xmlPath = xmlPath;
    }

    bool XMLSerialize::load(const char *name, const char *xmlPath)
    {
        doc.LoadFile(xmlPath);

        if (doc.Error())
        {
            return false;
        }

        XMLElement *root = doc.RootElement();
        pos = root->FirstChildElement(name);
        if (!pos)
        {
            return false;
        }

        return true;
    }

    void XMLSerialize::save()
    {
        XMLError a = doc.SaveFile(xmlPath);
    }

    void XMLSerialize::write(bool val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        const char *s = val ? "1" : "0";
        newNode->SetAttribute("val", s);
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(char val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("val", val);
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(int val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("val", std::to_string(val).c_str());
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(float val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("val", std::to_string(val).c_str());
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(double val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("val", std::to_string(val).c_str());
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(const char *val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("val", val);
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(std::string val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("val", val.c_str());
        pos->InsertEndChild(newNode);
    }

    void XMLSerialize::write(UserTypeXml &obj, const char *name)
    {
        obj.serialize(*this, name);
    }

    template <typename T>
    void XMLSerialize::write(std::vector<T> &val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("size", val.size());
        pos->InsertEndChild(newNode);
        XMLElement *pre = pos;
        pos = newNode;
        for (auto it = val.begin(); it != val.end(); it++)
        {
            write(*it, "ele");
        }
        pos = pre;
    }

    template <typename T>
    void XMLSerialize::write(std::list<T> &val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("size", val.size());
        pos->InsertEndChild(newNode);
        XMLElement *pre = pos;
        pos = newNode;
        for (auto it = val.begin(); it != val.end(); it++)
        {
            write(*it, "ele");
        }
        pos = pre;
    }

    template <typename T>
    void XMLSerialize::write(std::set<T> &val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("size", val.size());
        pos->InsertEndChild(newNode);
        XMLElement *pre = pos;
        pos = newNode;
        for (auto it = val.begin(); it != val.end(); it++)
        {
            write(*it, "ele");
        }
        pos = pre;
    }

    template <typename K, typename V>
    void XMLSerialize::write(std::map<K, V> &val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        newNode->SetAttribute("size", val.size());
        newNode->SetAttribute("type", "map");
        pos->InsertEndChild(newNode);
        XMLElement *pre = pos;
        pos = newNode;
        for (auto it = val.begin(); it != val.end(); it++)
        {
            write(it->first, "key");
            write(it->second, "value");
        }
        pos = pre;
    }

    template <typename F, typename S>
    void XMLSerialize::write(std::pair<F, S> &val, const char *name)
    {
        XMLElement *newNode = doc.NewElement(name);
        pos->InsertEndChild(newNode);
        XMLElement *pre = pos;
        pos = newNode;
        write(val.first, "first");
        write(val.second, "second");
        pos = pre;
    }

    template <typename T, typename... Args>
    void XMLSerialize::write_args(const T &value, const Args &...args)
    {
        write(value, "mem");
        write_args(args...);
    }

    void XMLSerialize::write_args()
    {
    }

    bool XMLSerialize::read(bool &val)
    {
        val = pos->Attribute("val") == "0" ? 0 : 1;
        return true;
    }

    bool XMLSerialize::read(char &val)
    {
        val = (pos->Attribute("val"))[0];
        return true;
    }

    bool XMLSerialize::read(int &val)
    {
        val = atoi(pos->Attribute("val"));
        return true;
    }

    bool XMLSerialize::read(float &val)
    {
        val = atof(pos->Attribute("val"));
        return true;
    }

    bool XMLSerialize::read(double &val)
    {
        val = atof(pos->Attribute("val"));
        return true;
    }

    bool XMLSerialize::read(std::string &val)
    {
        val = pos->Attribute("val");
        return true;
    }

    bool XMLSerialize::read(UserTypeXml &val)
    {
        val.deserialize(*this);
        return true;
    }

    template <typename T>
    bool XMLSerialize::read(std::vector<T> &val)
    {
        const char *n_str = pos->Attribute("size");
        if (n_str == NULL)
        {
            return false;
        }
        int n = atoi(n_str);
        val.resize(n);
        XMLElement *pre = pos;
        pos = pos->FirstChildElement("ele");
        if (pos == NULL)
        {
            return false;
        }
        auto it = val.begin();
        read(*it);
        for (it++; it != val.end(); it++)
        {
            pos = pos->NextSiblingElement();
            read(*it);
        }
        pos = pre;
        return true;
    }

    template <typename T>
    bool XMLSerialize::read(std::list<T> &val)
    {
        const char *n_str = pos->Attribute("size");
        if (n_str == NULL)
        {
            return false;
        }
        int n = atoi(n_str);
        val.resize(n);
        XMLElement *pre = pos;
        pos = pos->FirstChildElement("ele");
        if (pos == NULL)
        {
            return false;
        }
        auto it = val.begin();
        read(*it);
        for (it++; it != val.end(); it++)
        {
            pos = pos->NextSiblingElement();
            read(*it);
        }
        pos = pre;
        return true;
    }

    template <typename T>
    bool XMLSerialize::read(std::set<T> &val)
    {
        const char *n_str = pos->Attribute("size");
        if (n_str == NULL)
        {
            return false;
        }
        int n = atoi(n_str);
        val.resize(n);
        XMLElement *pre = pos;
        pos = pos->FirstChildElement("ele");
        if (pos == NULL)
        {
            return false;
        }
        auto it = val.begin();
        read(*it);
        for (it++; it != val.end(); it++)
        {
            pos = pos->NextSiblingElement();
            read(*it);
        }
        pos = pre;
        return true;
    }

    template <typename K, typename V>
    bool XMLSerialize::read(std::map<K, V> &val)
    {
        if (strcmp(pos->Attribute("type"), "map") != 0)
        {
            return false;
        }
        const char *n_str = pos->Attribute("size");
        if (n_str == NULL)
        {
            return false;
        }
        int n = atoi(n_str);
        val.clear();

        XMLElement *pre = pos;
        pos = pos->FirstChildElement("key");
        if (pos == NULL)
        {
            return false;
        }
        K key;
        V value;
        read(key);
        pos = pos->NextSiblingElement();
        read(value);
        val[key] = value;

        for (int i = 1; i < n; i++)
        {
            pos = pos->NextSiblingElement();
            read(key);
            pos = pos->NextSiblingElement();
            read(value);
            val[key] = value;
        }
        pos = pre;
        return true;
    }

    template <typename F, typename S>
    bool XMLSerialize::read(std::pair<F, S> &val)
    {
        XMLElement *pre = pos;
        pos = pos->FirstChildElement("first");
        if (pos == NULL)
        {
            return false;
        }
        read(val.first);
        pos = pos->NextSiblingElement();
        read(val.second);
        pos = pre;
        return true;
    }

    template <typename T, typename... Args>
    bool XMLSerialize::read_args(T &value, Args &...args)
    {
        read(value);
        pos = pos->NextSiblingElement();
        if (pos == NULL)
        {
            return false;
        }
        read_args(args...);
        return true;
    }

    bool XMLSerialize::read_args()
    {
        return true;
    }

    // serialize object obj to an XML file with the name
    template <typename T>
    void serialize_xml(T value, const char *name, const char *xmlPath)
    {
        XMLSerialize xs;
        xs.cover(name, xmlPath); // 这里传入name是为了覆盖
        xs.write(value, name);
        xs.save();
    }
    // reconstruct object from the content of xml file
    template <typename T>
    bool deserialize_xml(T &value, const char *name, const char *xmlPath)
    {
        XMLSerialize xs;
        xs.load(name, xmlPath);
        xs.read(value);
        return true;
    }

}